/*
 * Copyright (c) 2023-2023 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.spring.authorizationserver.web;

import java.security.Principal;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * 授权同意端点 copp <a
 * href="https://github.com/spring-projects/spring-authorization-server/blob/1.2.0/samples/demo-authorizationserver/src/main/java/sample/web/AuthorizationConsentController.java">...</a>
 *
 * @author zeng
 */
@Controller
@RequiredArgsConstructor
public class AuthorizationConsentController {
  private final RegisteredClientRepository registeredClientRepository;
  private final OAuth2AuthorizationConsentService authorizationConsentService;

  /**
   * oauth2 授权控制器
   *
   * @return consent.html
   */
  @GetMapping(value = "/oauth2/consent")
  public String consent(
      Principal principal,
      Model model,
      @RequestParam(OAuth2ParameterNames.CLIENT_ID) String clientId,
      @RequestParam(OAuth2ParameterNames.SCOPE) String scope,
      @RequestParam(OAuth2ParameterNames.STATE) String state,
      @RequestParam(name = OAuth2ParameterNames.USER_CODE, required = false) String userCode) {
    // 删除已同意的scope
    Set<String> scopesToApprove = new HashSet<>();
    Set<String> previouslyApprovedScopes = new HashSet<>();
    RegisteredClient registeredClient = this.registeredClientRepository.findByClientId(clientId);
    OAuth2AuthorizationConsent currentAuthorizationConsent =
        this.authorizationConsentService.findById(registeredClient.getId(), principal.getName());
    Set<String> authorizedScopes;
    if (currentAuthorizationConsent != null) {
      authorizedScopes = currentAuthorizationConsent.getScopes();
    } else {
      authorizedScopes = Collections.emptySet();
    }
    for (String requestedScope : StringUtils.delimitedListToStringArray(scope, " ")) {
      if (OidcScopes.OPENID.equals(requestedScope)) {
        continue;
      }
      if (authorizedScopes.contains(requestedScope)) {
        previouslyApprovedScopes.add(requestedScope);
      } else {
        scopesToApprove.add(requestedScope);
      }
    }
    model.addAttribute("clientId", clientId);
    model.addAttribute("state", state);
    model.addAttribute("scopes", withDescription(scopesToApprove));
    model.addAttribute("previouslyApprovedScopes", withDescription(previouslyApprovedScopes));
    model.addAttribute("principalName", principal.getName());
    model.addAttribute("userCode", userCode);
    if (StringUtils.hasText(userCode)) {
      model.addAttribute("requestURI", "/oauth2/device_verification");
    } else {
      model.addAttribute("requestURI", "/oauth2/authorize");
    }
    return "consent";
  }

  private static Set<ScopeWithDescription> withDescription(Set<String> scopes) {
    Set<ScopeWithDescription> scopeWithDescriptions = new HashSet<>();
    for (String scope : scopes) {
      scopeWithDescriptions.add(new ScopeWithDescription(scope));
    }
    return scopeWithDescriptions;
  }

  /** the is ScopeWithDescription */
  public static class ScopeWithDescription {
    private static final String DEFAULT_DESCRIPTION = "UNKNOWN SCOPE - 我们无法提供有关此权限的信息，授予此权限时请小心";
    private static final Map<String, String> scopeDescriptions = new HashMap<>();

    static {
      scopeDescriptions.put(OidcScopes.PROFILE, "此应用程序将能够读取您的个人资料信息。");
      scopeDescriptions.put("message.read", "此应用程序将能够读取您的邮件。");
      scopeDescriptions.put("message.write", "此应用程序将能够添加新消息。它还可以编辑和删除现有消息。");
      scopeDescriptions.put("other.scope", "这是范围描述的另一个范围示例。");
    }

    public final String scope;
    public final String description;

    ScopeWithDescription(String scope) {
      this.scope = scope;
      this.description = scopeDescriptions.getOrDefault(scope, DEFAULT_DESCRIPTION);
    }
  }
}
